#!/usr/bin/env bash
# Usage: swiftenv install <version>
# completes

set -e

# Check if the given version is already installed
check_installed() {
  local VERSION
  VERSION="$1"

  local PREFIX
  PREFIX="$(swiftenv-prefix "$VERSION" || true)"
  if [ -d "$PREFIX" ]; then
    echo "$VERSION is already installed."

    if [ -n "$SKIP_EXISTING" ]; then
      update_version_files
      exit 0
    fi

    exit 1
  fi
}

update_version_files() {
  if ([ "$set_global" == "auto" ] && [ "$set_local" == "false" ]); then
    swiftenv-global "$VERSION"
  elif ([ "$set_global" == "true" ]); then
    swiftenv-global "$VERSION"
  fi

  if [ "$set_local" == "true" ]; then
    swiftenv-local "$VERSION"
  fi
}

get_os_release() {
  test -e /etc/os-release && os_release='/etc/os-release' || os_release='/usr/lib/os-release'
  . "${os_release}"

  if [ "$ID" = "amzn" ]; then
    echo "amazonlinux$VERSION_ID"
  else
    echo $ID$VERSION_ID
  fi
}

get_platform() {
  if [ -n "$SWIFTENV_PLATFORM" ]; then
    echo "$SWIFTENV_PLATFORM"
    return
  fi

  if [ -n "$PLATFORM" ]; then
    echo "$PLATFORM"
    return
  fi

  case $(uname) in
  'Linux' )
    get_os_release
    ;;
  'Darwin' )
    echo "osx"
    ;;
  'MINGW64_NT-10.0'* )
    echo "windows10"
    ;;
  * )
    echo "unsupported-platform"
    ;;
  esac
}

install_binary() {
  local VERSION
  local URL
  VERSION="$1"
  URL="$2"

  if [[ "$URL" = *".pkg" ]]; then
    if [ "$(uname)" != "Darwin" ]; then
      echo "Cannot install .pkg from $URL on non macOS platform $(uname)."
      exit 1
    fi

    install_pkg_binary "$URL"
  elif [[ "$URL" = *".tar.gz" ]]; then
    install_tar_binary "$VERSION" "$URL"
  elif [[ "$URL" = *".exe" ]]; then
    install_exe_binary "$URL"
  else
    echo "swiftenv does not know how to install $URL. Only tar.gz and pkg files are supported."
    exit 1
  fi
}

download() {
  local URL
  local FILE
  URL=$1
  FILE=$2
  if ! [ -r "$FILE" ]; then
    curl -C - -Lo "$FILE.download" "$URL"
    mv "$FILE.download" "$FILE"
  fi
}

# Install a tarball binary from the supplied URL
install_tar_binary() {
  local VERSION
  local URL
  VERSION="$1"
  URL="$2"

  INSTALL_TMP="$TMPDIR/swiftenv-$VERSION-$USER"

  mkdir -p "$INSTALL_TMP"

  echo "Downloading $URL"

  pushd "$INSTALL_TMP"
  download "$URL" "swift-$VERSION.tar.gz"

  if $verify; then
    curl -o "swift-$VERSION.sig" "$URL.sig"
    gpg --verify "swift-$VERSION.sig"
  fi

  tar xzf "swift-$VERSION.tar.gz"
  popd

  DESTINATION="$SWIFTENV_ROOT/versions/$VERSION"
  # Some archives don't have an enclosing directory so check "usr" exists first
  if [ -d "$INSTALL_TMP/usr" ]; then
    mkdir -p "$DESTINATION"
    mv "$INSTALL_TMP/usr" "$DESTINATION"
  else
    mv "$INSTALL_TMP/swift-$VERSION_RELEASE"*/ "$DESTINATION"
  fi

  if $clean; then
    rm -fr "$INSTALL_TMP"
  fi
}

# Installs an `.pkg` binary from the supplied URL
install_pkg_binary() {
  local URL
  URL="$1"
  PKG="$TMPDIR/swiftenv-$VERSION-$USER.pkg"

  download "$URL" "$PKG"

  if $user; then
    installer -pkg "$PKG" -target CurrentUserHomeDirectory
  else
    sudo installer -pkg "$PKG" -target LocalSystem
  fi

  if $clean; then
    rm -fr "$PKG"
  fi
}

install_exe_binary() {
  local URL
  URL="$1"

  #powershell -Command {Install-Binary -Url "$URL" -Name "installer.exe" -ArgumentList ("-q")}
  echo "swiftenv does not yet support installing on Windows"
  exit 1
}

# Install the given version from source
install_source() {
  VERSION="$1"

  OPTIONS=""

  if $clean; then
    OPTIONS="--clean"
  else
    OPTIONS="--no-clean"
  fi

  if $verbose; then
    OPTIONS="$OPTIONS --verbose"
  fi

  swiftenv-build $OPTIONS "$VERSION" "$SWIFTENV_ROOT/versions/$VERSION"
}

build_version() {
  if [ -n "$url" ]; then
    echo 'The given URL must be to a binary version of Swift, you cannot use the `--build` option with a URL.'
    exit 1
  fi

  if [ -r "$SWIFTENV_SOURCE_PATH/share/swiftenv-build/$VERSION" ]; then
    vlog "Building $VERSION from source..."
    install_source "$VERSION"
    echo "$VERSION has been installed."
    swiftenv-rehash
    swiftenv-global "${VERSION##swift-}"
    exit 0
  fi

  echo "We don't have build instructions for $VERSION."
  exit 1
}

find_binary_url_from_api() {
  platform=$(get_platform)
  vlog "Checking for a URL for Swift $VERSION on $platform."

  extension="tar.gz"
  platform_directory="${platform/.}"

  if [ "$platform" == "osx" ]; then
    extension="pkg"
    platform_directory="xcode"
  elif [ "$platform" == "windows10" ]; then
    platform_directory=windows10
    platform=windows10
    extension=exe
  fi

  architecture=""
  if [ "$platform" != "osx" ] || [ "$platform" != "windows10" ]; then
    # macOS uses dual architecture binaries
    # FIXME windows10 - no support just yet

    if command -v "lscpu" >/dev/null 2>&1; then
      cpu_arch=$(LC_ALL=C lscpu | grep Architecture | cut -d ":" -f2 | tr -d " ")
      if [ "$cpu_arch" != "x86_64" ]; then
        architecture="-$cpu_arch"
        platform_directory+="-$cpu_arch"
      fi
    fi
  fi

  case "$VERSION" in
    *'-DEVELOPMENT-SNAPSHOT'* )
      VERSION_NUMBER=$(echo "$VERSION" | cut -d "-" -f1)
      url="https://download.swift.org/swift-$VERSION_NUMBER-branch/$platform_directory/swift-$VERSION/swift-$VERSION-$platform$architecture.$extension" ;;
    "DEVELOPMENT-SNAPSHOT"* )
      url="https://download.swift.org/development/$platform_directory/swift-$VERSION/swift-$VERSION-$platform$architecture.$extension" ;;
    * )
      url="https://download.swift.org/swift-$VERSION-release/$platform_directory/swift-$VERSION-RELEASE/swift-$VERSION-RELEASE-$platform$architecture.$extension" ;;
  esac

  vlog "Trying URL: $url"
}

sort_versions() {
  sed 'h; s/[+-]/./g; s/.p\([[:digit:]]\)/.z\1/; s/$/.z/; G; s/\n/ /' | \
    LC_ALL=C sort -t. -k 1,1 -k 2,2n -k 3,3n -k 4,4n -k 5,5n | awk '{print $2}'
}

get_version_list() {
  platform="$(get_platform)"

  if $snapshots; then
    curl -H 'Accept: text/plain' "https://swiftenv-api.fuller.li/versions?snapshot=true&platform=$platform"
    exit
  fi

  CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/swiftenv"
  CACHE_FILE="$CACHE_DIR/versions-${platform}.txt"

  if $refresh; then
    vlog "Updating version cache"
    # FIXME: Add support for architecture to swiftenv api
    mkdir -p $CACHE_DIR
    curl -s -H 'Accept: text/plain' "https://swiftenv-api.fuller.li/versions?platform=$platform" > "$CACHE_FILE"
  fi

  if [ -r "$CACHE_FILE" ] && [ $build != "true" ]; then
    cat "$CACHE_FILE"
  else
    swiftenv-build --definitions
  fi

  exit
}

clean=true
list=false
refresh=true
snapshots=false
build=auto
verbose=false
verify=false
set_global=auto
set_local=false
completes=false
user=true

if [ "$(uname)" == "Darwin" ]; then
  user=false
fi

if [ -n "$SWIFTENV_VERIFY" ] && [ "$SWIFTENV_VERIFY" != "false" ]; then
  verify=true
fi

unset SKIP_EXISTING

for args in "$@"; do
  if [ "$args" = "--no-clean" ]; then
    clean=false
  elif [ "$args" = "--clean" ]; then
    clean=true
  elif [ "$args" = "--list" ] || [ "$args" = "-l" ];  then
    list=true
  elif [ "$args" = "--list-snapshots" ]; then
    list=true
    snapshots=true
  elif [ "$args" = "--refresh" ]; then
    refresh=true
  elif [ "$args" = "--no-refresh" ]; then
    refresh=false
  elif [ "$args" = "--complete" ]; then
    refresh=false
    completes=true
  elif [ "$args" = "--build" ]; then
    build=true
  elif [ "$args" = "--no-build" ]; then
    build=false
  elif [ "$args" = "--skip-existing" ] || [ "$args" = "-s" ]; then
    SKIP_EXISTING=true
  elif [ "$args" = "--verbose" ]; then
    verbose=true
  elif [ "$args" = "--no-verbose" ]; then
    verbose=false
  elif [ "$args" = "--verify" ]; then
    verify=true
  elif [ "$args" = "--no-verify" ]; then
    verify=false
  elif [ "$args" = "--no-set-global" ]; then
    set_global=false
  elif [ "$args" = "--no-set-local" ]; then
    set_local=false
  elif [ "$args" = "--set-global" ]; then
    set_global=true
  elif [ "$args" = "--set-local" ]; then
    set_local=true
  elif [ "$args" = "--user" ]; then
    user=true
  elif [ "$args" = "--no-user" ]; then
    user=false
  else
    VERSION="$args"
  fi

  shift
done

vlog() {
  if $verbose; then
    echo "$1"
  fi
}

if $completes; then
  echo "--list"
  echo "--list-snapshots"
  echo "--refresh"
  echo "--no-refresh"
  echo "--verbose"
  echo "--no-verbose"
  echo "--verify"
  echo "--no-verify"
  echo "--clean"
  echo "--no-clean"
  echo "--set-global"
  echo "--no-set-global"
  echo "--set-local"
  echo "--no-set-local"
  echo "--user"
  echo "--no-user"
  get_version_list
  exit
fi

if $list; then
  get_version_list
fi

mkdir -p "$SWIFTENV_ROOT/versions"
if [ -z "$VERSION" ] ; then
  VERSION="$(swiftenv-version-name --dont-check)"

  if [ "$set_global" == "auto" ]; then
    set_global=false
  fi

  if [ "$VERSION" == "system" ]; then
    echo "Usage: swiftenv install <version>"
    exit 1
  fi
fi

if [ -z "$TMPDIR" ] ; then
  export TMPDIR=/tmp
fi

if [[ "$VERSION" == "https://"* ]]; then
  url="$VERSION"
  VERSION="${url##*/}"
  VERSION="${VERSION%-*}"
fi

VERSION="${VERSION##swift-}"

check_installed "$VERSION"

if ! $user; then
  if [ "$(uname)" != "Darwin" ]; then
    echo "--no-user installation is only supported on macOS."
    exit 1
  fi
fi

# Install Binary
if ([ "$build" == "auto" ] || [ "$build" == "false" ]); then
  if [ -z "$url" ]; then
    find_binary_url_from_api
  fi

  # verify URL points to valid file
  status_code=$(curl -I -s -o /dev/null -w "%{http_code}" $url || true)
  if [ "$status_code" == "404" ]; then
    unset url
  fi

  if [ "$url" ]; then
    install_binary "$VERSION" "$url"
  elif [ "$build" == "false" ]; then
    echo "Could not find a binary release of $VERSION."
    exit 1
  elif [ "$build" == "auto" ]; then
    build="true"
  fi
fi


# Install Source
if [ "$build" == "true" ]; then
  build_version
fi

echo "$VERSION has been installed."
swiftenv-rehash

update_version_files
